package com.hahaliu.hahaliu.service.impl.user;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hahaliu.hahaliu.common.constant.FavoriteConstant;
import com.hahaliu.hahaliu.common.constant.UserConstant;
import com.hahaliu.hahaliu.common.context.CurrentUser;
import com.hahaliu.hahaliu.common.exception.AccountHasExistsException;
import com.hahaliu.hahaliu.common.exception.AccountHasLogoutException;
import com.hahaliu.hahaliu.common.exception.AccountLockedException;
import com.hahaliu.hahaliu.common.exception.BaseException;
import com.hahaliu.hahaliu.common.utils.AliOssUtil;
import com.hahaliu.hahaliu.common.utils.JwtUtil;
import com.hahaliu.hahaliu.common.utils.MinioUtil;
import com.hahaliu.hahaliu.common.utils.RedisUtil;
import com.hahaliu.hahaliu.mapper.FavoriteMapper;
import com.hahaliu.hahaliu.mapper.MsgUnreadMapper;
import com.hahaliu.hahaliu.mapper.UserMapper;
import com.hahaliu.hahaliu.pojo.dto.UserDTO;
import com.hahaliu.hahaliu.pojo.dto.UserLoginDTO;
import com.hahaliu.hahaliu.pojo.dto.UserRegisterDTO;
import com.hahaliu.hahaliu.pojo.entity.Favorite;
import com.hahaliu.hahaliu.pojo.entity.MsgUnread;
import com.hahaliu.hahaliu.pojo.entity.User;
import com.hahaliu.hahaliu.pojo.entity.Video;
import com.hahaliu.hahaliu.service.user.UserService;
import com.hahaliu.hahaliu.service.video.VideoService;
import com.hahaliu.hahaliu.service.video.VideoStatsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author Liubuzhu
* @description 针对表【user】的数据库操作Service实现
* @createDate 2024-04-27 17:34:22
*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService{

	@Autowired
	private UserService userService;

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private AliOssUtil aliOssUtil;

	@Autowired
	private MinioUtil minioUtil;

	@Autowired
	private RedisUtil redisUtil;

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private MsgUnreadMapper msgUnreadMapper;

	@Autowired
	private JwtUtil jwtUtil;

	@Autowired
	private FavoriteMapper favoriteMapper;

	@Autowired
	private VideoService videoService;

	@Autowired
	private VideoStatsService videoStatsService;

	@Autowired
	private CurrentUser currentUser;

	@Autowired
	private AuthenticationProvider authenticationProvider;

	@Autowired
	@Qualifier("taskExecutor")
	private Executor taskExecutor;

	/**
	 * 用户登录
	 * @param userLoginDTO
	 * @return
	 */
	@Override
	public User login(UserLoginDTO userLoginDTO) {

		String username = userLoginDTO.getUsername();
        String password = userLoginDTO.getPassword();

		//验证是否能正常登录
		//将用户名和密码封装成一个类，这个类不会存明文了，将是加密后的字符串
		UsernamePasswordAuthenticationToken authenticationToken =
				new UsernamePasswordAuthenticationToken(username, password);

		// 用户名或密码错误会抛出异常
		Authentication authenticate;
		try {
			authenticate = authenticationProvider.authenticate(authenticationToken);
		} catch (Exception e) {
			throw new BaseException(UserConstant.ACCOUNT_OR_PASSWORD_ERROR);
		}

		//将用户取出来
		UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
		User user = loginUser.getUser();

		// 顺便更新redis中的数据
		redisUtil.setExObjectValue("user:" + user.getUId(), user);  // 默认存活1小时

		if (Objects.equals(user.getState(), UserConstant.DISABLE)) {
			// 账号被封禁
			throw new AccountLockedException(UserConstant.ACCOUNT_LOCKED);
		}

		if (Objects.equals(user.getState(), UserConstant.LOGOUT)) {
			// 账号被注销
			throw new AccountHasLogoutException(UserConstant.ACCOUNT_HAS_LOGOUT);
		}

		try {
			// 把完整的用户信息存入redis，时间跟token一样，注意单位
			// 这里缓存的user信息建议只供读取uid用，其中的状态等非静态数据可能不准，所以 redis另外存值
			redisUtil.setExObjectValue("security:user:" + user.getUId(), user, 60L * 60 * 24 * 2, TimeUnit.SECONDS);
			// 将该用户放到redis中在线集合
//            redisUtil.addMember("login_member", user.getUid());
		} catch (Exception e) {
			log.error("存储redis数据失败");
			throw e;
		}

		//3、返回实体对象
		return user;
	}

	/**
	 * 管理员登录
	 * @param userLoginDTO
	 * @return
	 */
	public User adminLogin(UserLoginDTO userLoginDTO) {
		String username = userLoginDTO.getUsername();
		String password = userLoginDTO.getPassword();

		//验证是否能正常登录
		//将用户名和密码封装成一个类，这个类不会存明文了，将是加密后的字符串
		UsernamePasswordAuthenticationToken authenticationToken =
				new UsernamePasswordAuthenticationToken(username, password);

		// 用户名或密码错误会抛出异常
		Authentication authenticate;
		try {
			authenticate = authenticationProvider.authenticate(authenticationToken);
		} catch (Exception e) {
			throw new BaseException(UserConstant.ACCOUNT_OR_PASSWORD_ERROR);
		}

		//将用户取出来
		UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal();
		User user = loginUser.getUser();

		// 顺便更新redis中的数据
		redisUtil.setExObjectValue("user:" + user.getUId(), user);  // 默认存活1小时

		if (Objects.equals(user.getState(), UserConstant.DISABLE)) {
			// 账号被封禁
			throw new AccountLockedException(UserConstant.ACCOUNT_LOCKED);
		}

		if (Objects.equals(user.getRole(), UserConstant.NORMAL_USER)) {
			// 非管理员用户
			throw new BaseException("您不是管理员哦！");
		}

		if (Objects.equals(user.getState(), UserConstant.LOGOUT)) {
			// 账号注销
			throw new AccountHasLogoutException(UserConstant.ACCOUNT_HAS_LOGOUT);
		}

		try {
			redisUtil.setExObjectValue("security:admin:" + user.getUId(), user, 60L * 60 * 24 * 2, TimeUnit.SECONDS);
		} catch (Exception e) {
			log.error("存储redis数据失败");
			throw e;
		}

		//3、返回实体对象
		return user;
	}

	/**
	 * 新增用户
	 * @param userRegisterDTO
	 */
	public void register(UserRegisterDTO userRegisterDTO) {

		// 获取用户名和密码
		String username = userRegisterDTO.getUsername();
        String password = userRegisterDTO.getPassword();

		// 创建查询条件，查询数据库中是否存在相同用户名且未注销的用户
		QueryWrapper<User> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("username", username);
		queryWrapper.ne("state", 2);
		User user = userMapper.selectOne(queryWrapper);   //查询数据库里值等于username并且没有注销的数据
		if (user != null) {
			throw new AccountHasExistsException(UserConstant.ALREADY_EXISTS);
		}

		// 查询最后一个用户的ID，用于生成新用户的ID
		QueryWrapper<User> queryWrapper1 = new QueryWrapper<>();
		queryWrapper1.orderByDesc("u_id").last("limit 1");    // 降序选第一个
		User last_user = userMapper.selectOne(queryWrapper1);
		int new_user_uid;
		if (last_user == null) {
			new_user_uid = 1;
		} else {
			new_user_uid = last_user.getUId() + 1;
		}

		//对前端传过来的明文密码进行密文存储
		String encodedPassword = passwordEncoder.encode(password);

		User registerUser = new User();
		//对象属性拷贝
		BeanUtils.copyProperties(userRegisterDTO, registerUser);

		//设置当前记录创建时间
		registerUser.setCreateDate(LocalDateTime.now());
		registerUser.setPassword(encodedPassword);

		//设置默认的信息
		registerUser.setNickname("用户_" + new_user_uid);
		registerUser.setState(UserConstant.NORMAL);
		registerUser.setGender(UserConstant.UNKNOWN);
		registerUser.setDescription("这个人很懒，什么都没留下~");
		registerUser.setAvatar("http://127.0.0.1:9000/haha-billbill/image/avatar/ED269C66C7653F222A5C3E427784E355.png");
		registerUser.setCoin(0);
		registerUser.setExp(0);
		registerUser.setAuth(UserConstant.ORDINARY_USER);
		registerUser.setAuthMsg("");
		registerUser.setVip(UserConstant.NORMAL_VIP);
		registerUser.setRole(UserConstant.NORMAL_USER);

		userMapper.insert(registerUser);

		// 给用户创建默认收藏夹
		QueryWrapper<User>  queryWrapper2 = new QueryWrapper<>();
		queryWrapper2.eq("username", username);
        User user1 = userMapper.selectOne(queryWrapper2);

		Favorite favorite = new Favorite(null, user1.getUId(), FavoriteConstant.DEFAULT_FAVORITE, FavoriteConstant.VISIBLE, null, "默认收藏夹", "这是一个默认收藏夹", 0, LocalDateTime.now(), FavoriteConstant.NOT_DELETE);
		favoriteMapper.insert(favorite);

		msgUnreadMapper.insert(new MsgUnread(user1.getUId(),0,0,0,0,0,0));
	}

	public void updateUserInfo(UserDTO userDTO) {
		Integer uId = currentUser.getUserId();
		String nickname = userDTO.getNickname();
		String description = userDTO.getDescription();
		Integer gender = userDTO.getGender();

		if (nickname == null || nickname.trim().isEmpty()) {
			throw new BaseException("昵称不能为空");
		}
		if (nickname.length() > 24 || description.length() > 100) {
			throw new BaseException("输入字符过长");
		}
		// 查重
		QueryWrapper<User> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("nickname", nickname).ne("u_id", uId);
		User user = userMapper.selectOne(queryWrapper);
		if (user != null) {
			throw new BaseException("该昵称已被其他用户占用");
		}
		UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("u_id", uId)
				.set("nickname", nickname)
				.set("description", description)
				.set("gender", gender);
		userMapper.update(null, updateWrapper);

	}

	public String updateUserAvatar (Integer uId, MultipartFile file) {
		log.info("文件上传:{}", file);
		// 原始文件名
		String originalFilename = file.getOriginalFilename();
		// 截取原始文件名的后缀
		String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
		// 构造新文件名称
		String objectName = UUID.randomUUID().toString() + extension;
		// 文件的请求路径
//			String filePath = aliOssUtil.upload(file.getBytes(), "image/avatar", objectName);
		String filePath = minioUtil.upload(file, "image/avatar", objectName);
		// TODO 后期打算首先判断文件是否存在，存在即删除后在上传
		// 判断文件是否存在
		
		// 更新数据库
		UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
		updateWrapper.eq("u_id", uId).set("avatar", filePath);
		userMapper.update(null, updateWrapper);

		return filePath;
	}

//	public String updateUserAvatar (Integer uId, MultipartFile file) {
//		log.info("文件上传:{}", file);
//		try {
//			// 原始文件名
//			String originalFilename = file.getOriginalFilename();
//			// 截取原始文件名的后缀
//			String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
//			// 构造新文件名称
//			String objectName = UUID.randomUUID().toString() + extension;
//			// 文件的请求路径
//			String filePath = aliOssUtil.upload(file.getBytes(), "image/avatar", objectName);
//			// 更新数据库
//			UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//			updateWrapper.eq("u_id", uId).set("avatar", filePath);
//			userMapper.update(null, updateWrapper);
//
//			return filePath;
//		} catch (IOException e) {
//			log.error("文件上传失败：{}", e);
//			throw new BaseException("文件上传失败：" + e.getMessage());
//		}
//	}

	public IPage<User> searchByNameLike(String name, Integer page, Integer quantity) {
		Page<User> pageInfo = new Page<>(page, quantity);

		QueryWrapper<User> queryWrapper = new QueryWrapper<>();
		queryWrapper.like("nickname", name);

		return userMapper.selectPage(pageInfo, queryWrapper);
	}

	@Override
	public UserDTO getUserById(Integer id) {
		// 从redis中获取最新数据
		User user = redisUtil.getObject("user:" + id, User.class);
		// 如果redis中没有user数据，就从mysql中获取并更新到redis
		if (user == null) {
			user = userMapper.selectById(id);
			if (user == null) {
				return null;    // 如果uid不存在则返回空
			}
			User finalUser = user;
			CompletableFuture.runAsync(() -> {
				redisUtil.setExObjectValue("user:" + finalUser.getUId(), finalUser);  // 默认存活1小时
			}, taskExecutor);
		}
		UserDTO userDTO = new UserDTO();
		userDTO.setUId(user.getUId());
		userDTO.setState(user.getState());
		if (user.getState() == 2) {
			userDTO.setNickname("账号已注销");
			userDTO.setAvatar("http://127.0.0.1:9000/haha-billbill/image/avatar/ED269C66C7653F222A5C3E427784E355.png");
			userDTO.setGender(2);
			userDTO.setDescription("-");
			userDTO.setExp(0);
			userDTO.setCoin(0);
			userDTO.setVip(0);
			userDTO.setAuth(0);
			userDTO.setVideoCount(0);
			userDTO.setFollowsCount(0);
			userDTO.setFansCount(0);
			userDTO.setLoveCount(0);
			userDTO.setPlayCount(0);
			return userDTO;
		}
		userDTO.setNickname(user.getNickname());
		userDTO.setAvatar(user.getAvatar());
		userDTO.setGender(user.getGender());
		userDTO.setDescription(user.getDescription());
		userDTO.setExp(user.getExp());
		userDTO.setCoin(user.getCoin());
		userDTO.setVip(user.getVip());
		userDTO.setAuth(user.getAuth());
		userDTO.setAuthMsg(user.getAuthMsg());
		userDTO.setFollowsCount(0);
		userDTO.setFansCount(0);
		Set<Object> set = redisUtil.zReverange("user_video_upload:" + user.getUId(), 0L, -1L);
		if (set == null || set.isEmpty()) {
			userDTO.setVideoCount(0);
			userDTO.setLoveCount(0);
			userDTO.setPlayCount(0);
			return userDTO;
		}

		// 并发执行每个视频数据统计的查询任务
		List<Video> list = set.stream().parallel()
				.map(vid -> videoStatsService.getVideoStatsById((Integer) vid))
				.collect(Collectors.toList());

		int videoCount = list.size(), love = 0, play = 0;
		for (Video video : list) {
			love = love + video.getGood();
			play = play + video.getPlay();
		}
		userDTO.setVideoCount(videoCount);
		userDTO.setLoveCount(love);
		userDTO.setPlayCount(play);
		return userDTO;
	}

	/**
	 * 获取用户个人信息
	 * @return CustomResponse对象
	 */
	@Override
	public UserDTO getPersonalInfo() {
		Integer loginUserId = currentUser.getUserId();
		UserDTO userDTO = userService.getUserById(loginUserId);

		// 检查账号状态，1 表示封禁中，不允许登录，2表示账号注销了
		if (userDTO.getState() == 2) {
			throw new BaseException("账号已注销");
		}
		if (userDTO.getState() == 1) {
			throw new BaseException("账号异常，封禁中");
		}

		return userDTO;
	}

}




